Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/学习/408/数据结构

7.1 查找的基本概念

12 分钟阅读 · Note

目录树 578 篇

          • 3.1 栈
          • 4.1 串定义与基本操作
          • 5.1 树
          • 6.1 图的基本概念
          • 7.1 查找的基本概念
          • 8.1 排序基本概念
          • 第二章 线性表
          • 第一章 绪论
      • 论文
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗3.1 栈同一路径↗4.1 串定义与基本操作同一路径↗5.1 树同一路径↗6.1 图的基本概念同一路径↗8.1 排序基本概念同一路径↗第二章 线性表同一路径
  • 7.1 查找的基本概念

7.1 查找的基本概念

![Pasted image 20251030214257](../../../_assets/5e0d871-Pasted image 20251030214257.png) 查找表并不是指某一种特定的数据结构,而是被查找的数据的一个称呼

  • 静态查找表:仅查找
  • 动态查找表:查找、插入与删除

![Pasted image 20251030214806](../../../_assets/c9a1bc9-Pasted image 20251030214806.png)

7.2 顺序与折半查找

7.2.1 顺序查找(线性查找)

从头找到尾 ![Pasted image 20251030215557](../../../_assets/2990c83-Pasted image 20251030215557.png)

添加哨兵: ![Pasted image 20251030215857](../../../_assets/979efd3-Pasted image 20251030215857.png)

![Pasted image 20251030220154](../../../_assets/efaff60-Pasted image 20251030220154.png)

使用顺序表优化

![Pasted image 20251030220626](../../../_assets/2f32e12-Pasted image 20251030220626.png)

7.2.2 二分查找(折半查找)

仅适用于有序顺序表,链表是不可能实现折半的 ![Pasted image 20251030221453](../../../_assets/83adb32-Pasted image 20251030221453.png) ![Pasted image 20251030221622](../../../_assets/9778309-Pasted image 20251030221622.png) ![Pasted image 20251030221734](../../../_assets/2209841-Pasted image 20251030221734.png) ![Pasted image 20251030221820](../../../_assets/2b91282-Pasted image 20251030221820.png)

![Pasted image 20251030222420](../../../_assets/1e152f6-Pasted image 20251030222420.png)

代码实现

![Pasted image 20251030222643](../../../_assets/e04b76f-Pasted image 20251030222643.png)

效率分析(判定树)

![Pasted image 20251030223107](../../../_assets/cdc96b6-Pasted image 20251030223107.png) ![Pasted image 20251030223718](../../../_assets/e05594d-Pasted image 20251030223718.png)

![Pasted image 20251030223516](../../../_assets/a51afed-Pasted image 20251030223516.png) ![Pasted image 20251030223548](../../../_assets/43daf67-Pasted image 20251030223548.png)

![Pasted image 20251030223833](../../../_assets/25938e0-Pasted image 20251030223833.png)

7.2.3 分块查找(索引顺序查找)

使用存储着每个区间最大值的索引表

![Pasted image 20251031160146](../../../_assets/65d5aad-Pasted image 20251031160146.png) ![Pasted image 20251031160326](../../../_assets/206bf9a-Pasted image 20251031160326.png)

可以结合折半查找,在这里就和普通的折半查找不同。 ![Pasted image 20251031161419](../../../_assets/4a77e47-Pasted image 20251031161419.png) 移动后是这样的,正常应该是结束查找。但是应在low中查找。 ![Pasted image 20251031161111](../../../_assets/dfaa03f-Pasted image 20251031161111.png)

![Pasted image 20251031161953](../../../_assets/d937b3f-Pasted image 20251031161953.png)

ASL分析

在索引表顺序查找的情况下:手动模拟一遍就行 在索引表折半查找的情况下:同上,但是注意折半的规律 对于查找失败的情况更加复杂,一般不考

但是,查找表分块有规律: ![Pasted image 20251031163039](../../../_assets/0c85404-Pasted image 20251031163039.png)

![Pasted image 20251031163148](../../../_assets/66f1c91-Pasted image 20251031163148.png)

7.3 树型查找

7.3.1 二叉排序树(BST)![Pasted image 20251031170102](../../../_assets/5210dd3-Pasted image 20251031170102.png)

![Pasted image 20251031170310](../../../_assets/b691f4b-Pasted image 20251031170310.png) ![Pasted image 20251031170341](../../../_assets/89dd140-Pasted image 20251031170341.png)

查找

递归的实现方式会导致每一次递归在函数调用栈中新增一个任务,这导致O(h)的空间复杂度。 ![Pasted image 20251031170454](../../../_assets/9b8e3f8-Pasted image 20251031170454.png)

插入

递归实现: ![Pasted image 20251031170929](../../../_assets/757d729-Pasted image 20251031170929.png)

构造

![Pasted image 20251031171122](../../../_assets/7a2ab44-Pasted image 20251031171122.png)

删除

  1. 叶子结点:直接删除
  2. 只有左或右子树:使用其子树代替 ![Pasted image 20251031172227](../../../_assets/5cdc938-Pasted image 20251031172227.png)
  3. 同时具有左子树右子树:将左子树中的最大值(右子树中的最小值)取出,作为代替结点,被取出的结点的视为被删除,使用操作2. ![Pasted image 20251031172515](../../../_assets/f532d6b-Pasted image 20251031172515.png)

ASL

  • 成功:时间复杂度O(h),应该尽可能的保证树的高度最低(平衡) ![Pasted image 20251031172552](../../../_assets/9812b04-Pasted image 20251031172552.png) (1 * 1+2 * 2+3 * 4+4 * 1)/8=2.625

  • 失败: ![Pasted image 20251031172824](../../../_assets/cfb9ffd-Pasted image 20251031172824.png)

7.3.2 平衡二叉树(AVL)

树上的任意结点的平衡因子(左子树高 - 右子树高)为-1、0、1。也就是,左右子树的高度不能超过1 ![Pasted image 20251031173248](../../../_assets/7559946-Pasted image 20251031173248.png)

插入后调整树结构

每次调整最小不平衡子树,调整完毕后,其余的节点的平衡因子会自动的恢复正常: ![Pasted image 20251031173643](../../../_assets/2d5c9a7-Pasted image 20251031173643.png)

调整分为四种情况:

  1. LL:在结点的左孩子的左子树中插入导致不平衡 ![Pasted image 20251031174534](../../../_assets/f3252ea-Pasted image 20251031174534.png) 这里的所有子树的高度均为H,是因为只有在这种情况下插入节点才会导致A树成为最小不平衡子树。

  2. RR:在结点的右孩子的右子树中插入导致不平衡 ![Pasted image 20251031174932](../../../_assets/0b87d88-Pasted image 20251031174932.png)

  3. LR:在结点的左孩子的右子树中插入导致不平衡 ![Pasted image 20251031175603](../../../_assets/b6df374-Pasted image 20251031175603.png) 无论是插进入C的左边还是右边,都先C左旋,再将C右旋 ![Pasted image 20251031175746](../../../_assets/aaf8325-Pasted image 20251031175746.png)

其实不必一步步旋转,可以看做是对60结点的一个“提升” ![Pasted image 20251031180945](../../../_assets/692f0a9-Pasted image 20251031180945.png)

  1. RL:在结点的右孩子的左子树中插入导致不平衡 ![Pasted image 20251031180022](../../../_assets/e9c9025-Pasted image 20251031180022.png)

代码实现

![Pasted image 20251031175215](../../../_assets/1e7b23b-Pasted image 20251031175215.png)

ASL

![Pasted image 20251031181400](../../../_assets/6890fef-Pasted image 20251031181400.png)

删除

时间复杂度:O(log n)

  1. 删除结点,与二叉树一样。
  2. 向上寻找到最小不平衡子树,找不到就结束。
  3. 寻找最小不平衡子树根节的最高点子树,在此子树上重复一次操作,也就是找到个头最高的儿子,与这个儿子的最高的孙子。若果左右儿子、与左右孙子一样,可以随便选一个,结果一样。(在考试中,不可能考多可能性的)
  4. 根据孙子与祖先的位置关系,对儿子按LL、RR、RL与LR进行旋转,与插入的操作一样。 ![Pasted image 20251031191633](../../../_assets/c5bee29-Pasted image 20251031191633.png) ![Pasted image 20251031191812](../../../_assets/a26f11d-Pasted image 20251031191812.png)
  5. 如果有不平衡的现象,重复2操作。(408考察可能性较低) ![Pasted image 20251031192250](../../../_assets/1c73532-Pasted image 20251031192250.png) 发生了不平衡传到: ![Pasted image 20251031192330](../../../_assets/5db77d3-Pasted image 20251031192330.png) 重复步骤2: ![Pasted image 20251031192456](../../../_assets/e56db28-Pasted image 20251031192456.png) ![Pasted image 20251031192537](../../../_assets/4cee949-Pasted image 20251031192537.png) 不平衡传导结束,算法结束。

7.3.3 红黑树(RBT)

在平衡二叉树(AVL)中进行删除操作,可能会破坏平衡,需要频繁地进行树的形态调整,时间开销大。 而红黑树(RBT),很多时候不会被破坏平衡性,就算需要调整,也可以在常数级的时间内完成。

在经常查询的场景可以使用平衡二叉树,在频繁删除插入的场景使用红黑树。在现代开发过程中,最常使用RBT。

RBT本身是一种优化的BST ![Pasted image 20251031195713](../../../_assets/dc206d3-Pasted image 20251031195713.png) 左根右,根叶黑,不红红,黑路同 ![Pasted image 20251031195820](../../../_assets/338ec80-Pasted image 20251031195820.png)

性质

  1. 从根节点到叶结点的最长路径不大于最短路径的2倍
  2. 内部有n个节点的RBT的高度h<=2log(n+1) ![Pasted image 20251031201608](../../../_assets/fe1d846-Pasted image 20251031201608.png) 黑高h,内部结点至少有2^h -1 个 ![Pasted image 20251031210923](../../../_assets/76a17db-Pasted image 20251031210923.png) 树高h,根结点黑高>=h/2,此时内部最少的结点数为h^(h/2) - 1,也就是n>=h^(h/2) - 1得到h<=2log(n+1)

查找

与BST相同

插入

![Pasted image 20251031204108](../../../_assets/bf2d7a6-Pasted image 20251031204108.png)

例子:每次插入不会破坏左根右,根叶黑与黑路同,但只要有不红红,就要调整树的结构。 ![Pasted image 20251031205113](../../../_assets/e8e2ed0-Pasted image 20251031205113.png)

![Pasted image 20251031205437](../../../_assets/6aa91fd-Pasted image 20251031205437.png)

删除

  • 时间复杂度:O(log 2)
  • 与二叉排序树相同
  • 在破坏特性后,按找与插入相同的操作

7.4 B、B+树

7.4.1 B树(多路平衡查找树)

![Pasted image 20251031212447](../../../_assets/63953de-Pasted image 20251031212447.png)

查找效率的保证

![Pasted image 20251031213435](../../../_assets/9c3b3a1-Pasted image 20251031213435.png) ![Pasted image 20251031213455](../../../_assets/6fd2158-Pasted image 20251031213455.png) 在特殊情况下,比如一个元素,根结点就可能不满足有m/2向上取整个分叉。保证树不会太高。 ![Pasted image 20251031213905](../../../_assets/f73e133-Pasted image 20251031213905.png) 保证树的平衡

满足分叉与关键字数量、平衡要求的多叉树,就是B树。 ![Pasted image 20251031214353](../../../_assets/fbf35e4-Pasted image 20251031214353.png) ![Pasted image 20251031214544](../../../_assets/9501580-Pasted image 20251031214544.png)

特性

![Pasted image 20251031215328](../../../_assets/aa49e96-Pasted image 20251031215328.png) ![Pasted image 20251031214743](../../../_assets/2be51be-Pasted image 20251031214743.png)

插入

每次插入终端结点 ![Pasted image 20251031220319](../../../_assets/c60da27-Pasted image 20251031220319.png)

![Pasted image 20251031220338](../../../_assets/d57a894-Pasted image 20251031220338.png)

![Pasted image 20251031220440](../../../_assets/430d6a9-Pasted image 20251031220440.png) ![Pasted image 20251031220528](../../../_assets/76e5e6a-Pasted image 20251031220528.png)

![Pasted image 20251031220858](../../../_assets/48a5a02-Pasted image 20251031220858.png)

将提升的结点放置到,其父节点的分叉的后面: ![Pasted image 20251031220741](../../../_assets/4cf780d-Pasted image 20251031220741.png)

分裂后再分裂: ![Pasted image 20251031221308](../../../_assets/91ac903-Pasted image 20251031221308.png) ![Pasted image 20251031221413](../../../_assets/bc1c381-Pasted image 20251031221413.png)

![Pasted image 20251031221138](../../../_assets/df7c5fd-Pasted image 20251031221138.png)

删除

  1. 终端结点且满足数量下限:直接删除
  2. 根结点:寻找直接前驱或者直接后继代替且满足数量下限(与二叉排序树一样)。转换为了删除终端结点1操作。
  3. 不满足数量下限:
    1. 借兄弟:![Pasted image 20251031222309](../../../_assets/9784947-Pasted image 20251031222309.png)![Pasted image 20251031222616](../../../_assets/89892bb-Pasted image 20251031222616.png)
    2. 兄弟不够:![Pasted image 20251031222758](../../../_assets/72c28d8-Pasted image 20251031222758.png)![Pasted image 20251031222849](../../../_assets/84f301c-Pasted image 20251031222849.png)![Pasted image 20251031222941](../../../_assets/5e80fd7-Pasted image 20251031222941.png)

7.4.2 B+树

与分块查找类似 ![Pasted image 20251031224111](../../../_assets/88addc1-Pasted image 20251031224111.png)

![Pasted image 20251031223628](../../../_assets/386c794-Pasted image 20251031223628.png) 这里的非叶根结点,指不是第一种情况的根节点,即不同时是叶与根的根节点。这是为了保证绝对的平衡。

查找

![Pasted image 20251031224433](../../../_assets/eb26467-Pasted image 20251031224433.png) 也可以使用p指针来顺序查找。

与B树的区别

  1. B+树n个结点对应n个分叉,B树n+1个
  2. m阶B+树根节点可以取[1,m],其他结点[m/2向上取整,m],m阶B树根节点可以取[1,m-1],其他结点[m/2向上取整-1,m-1]
  3. B+树中,叶子结点包含全部的结点,并且内部结点可能重复,B树中各个节点是不重复的。
  4. 在B+树中,非叶子结点仅起到索引的作用,只有叶子节点的指针指向记录,B树中的结点包含记录 ![Pasted image 20251102164103](../../../_assets/35f12cf-Pasted image 20251102164103.png) 无论B+树还是B树,其中的每一个结点都存放在一个硬盘的一个一个块中,尽可能的使结点中的关键字数量更多,就能使OS的读取效率更高,度磁盘的次数更少,速度更快。

7.5 散列表(哈希表 Hash)

7.5.1 基本概念

![Pasted image 20251102164516](../../../_assets/7b7cb9e-Pasted image 20251102164516.png)

![Pasted image 20251102164836](../../../_assets/3b2c132-Pasted image 20251102164836.png)

7.5.2 散列函数的构造

在构造的过程中要尽可能的减少冲突的发生。

关键点

  1. 定义域要涵盖所有可能出现的关键字
  2. 值域不能超过散列表的合法地址范围
  3. 散列函数计算出的地址应该尽可能的均匀分布在整个散列表的地址空间,以减少冲突的发生
  4. 函数尽可能简单,提高计算效率

除留余数法

场景:通用方法

![Pasted image 20251102170116](../../../_assets/9ca5993-Pasted image 20251102170116.png)

![Pasted image 20251102170158](../../../_assets/01e3014-Pasted image 20251102170158.png)

直接定值法

场景:关键字基本连续分布

![Pasted image 20251102170437](../../../_assets/fec98e1-Pasted image 20251102170437.png)

数字分析法

场景:关键字集合已知,且某几个数分布均匀

![Pasted image 20251102170745](../../../_assets/fc84653-Pasted image 20251102170745.png)

平方取中法

场景:关键字的每一位的取值都不均匀

![Pasted image 20251102170901](../../../_assets/86947e1-Pasted image 20251102170901.png)

7.5.3 冲突处理

拉链法

将同义词存储在一个链表中 ![Pasted image 20251102171814](../../../_assets/c9365e8-Pasted image 20251102171814.png)

查找: ![Pasted image 20251102172123](../../../_assets/3150188-Pasted image 20251102172123.png)

开放定值法

如果发生冲突,就寻找一个新的空闲位置。 ![Pasted image 20251102172839](../../../_assets/bfa6b24-Pasted image 20251102172839.png)

![Pasted image 20251102172935](../../../_assets/e393985-Pasted image 20251102172935.png) d0的值为0,表示初始地址。0 <= i <= m-1

探测序列

  1. 线性探测法: ![Pasted image 20251102173408](../../../_assets/88762b5-Pasted image 20251102173408.png)

  2. 平方探测法 ![Pasted image 20251102173804](../../../_assets/f47bba3-Pasted image 20251102173804.png)

  3. 双散列法 ![Pasted image 20251102174042](../../../_assets/115755c-Pasted image 20251102174042.png)

  4. 伪随机序列法:人为手动提前编写伪随机序列

删除

![Pasted image 20251102174534](../../../_assets/f0283e8-Pasted image 20251102174534.png)如果散列表中有大量的已经删除的数据,会导致散列表看起来很满,但是实际很空的问题,导致查找的效率低下。可以不定期的整理散列表来避免。

7.5.4 ASL

![Pasted image 20251102175945](../../../_assets/a79dc38-Pasted image 20251102175945.png)

![Pasted image 20251102180245](../../../_assets/0795068-Pasted image 20251102180245.png)

找到第一个空位所需要的匹配次数: ![Pasted image 20251102181030](../../../_assets/2255e5c-Pasted image 20251102181030.png)

要以散列函数能够覆盖的地址的 长度为分母计算: ![Pasted image 20251102181220](../../../_assets/5425934-Pasted image 20251102181220.png)

![Pasted image 20251102181716](../../../_assets/3577d56-Pasted image 20251102181716.png) 失败的结果与(2)相同,成功的值的分母-1

装填因子

![Pasted image 20251102182256](../../../_assets/cd64627-Pasted image 20251102182256.png) ![Pasted image 20251102182314](../../../_assets/c97d576-Pasted image 20251102182314.png)

聚集(堆积)

在处理冲突的过程中,不同的初始散列地址的元素争夺同一个位置

![Pasted image 20251102182605](../../../_assets/ca05ed7-Pasted image 20251102182605.png) ![Pasted image 20251102182628](../../../_assets/efa2c63-Pasted image 20251102182628.png) ![Pasted image 20251102182802](../../../_assets/d40c598-Pasted image 20251102182802.png)

可以采用平方探测法来减少聚集现象: ![Pasted image 20251102183229](../../../_assets/d9ef47f-Pasted image 20251102183229.png)